#include <stdint.h>
#include <iostream>
#include <windows.h>
#include <string>
#include "iniparser.h"
#include "traverse_dir.h"

//---------------------------------------------------------------------------------------
int file_write(const char *file, int addr, const void *data, int len)
{
    const char *cData = (const char *)data;
    FILE *fp = fopen(file, "wb");
    if (!fp) return -1;

    fseek(fp,addr,SEEK_SET);
    fwrite(cData,1,len,fp);

    fclose(fp);
    return len;
}
int file_read(const char *file, int addr, void *data, int len)
{
    char *cData = (char *)data;
    FILE *fp = fopen(file, "rb");
    if (!fp) return -1;

    fseek(fp,addr,SEEK_SET);
    fread(cData,1,len,fp);

    fclose(fp);
    return len;
}

//---------------------------------------------------------------------------------------
struct Settings
{
    int bayer;
    int width;
    int height;
    string rawdir;
};
#define INI_FILE "settings.ini"
int get_settings(Settings &setting)
{
    dictionary *ini = iniparser_load(INI_FILE);
    if (ini == NULL)
    {
        cerr << "Error: Cannot parse setting file:" << INI_FILE << endl;
        return -1 ;
    }

    setting.bayer = iniparser_getint(ini, "Image:bayer", 2);
    setting.width = iniparser_getint(ini, "Image:width", 4192);
    setting.height = iniparser_getint(ini, "Image:height", 3104);
    setting.rawdir = iniparser_getstring(ini, "Image:rawdir", "");

    iniparser_freedict(ini);
    return 0 ;
}
//---------------------------------------------------------------------------------------
int save_image_as_raw(const char *file, void *image, int width, int height)
{
    return file_write(file, 0, image, width*height);
}

int save_image_as_bmp(const char *file, uint8_t *pBmp, int width,int height)
{
    typedef struct
    {
        long imageSize;
        long blank;
        long startPosition;
    }BmpHead;

    typedef struct
    {
        long  Length;
        long  width;
        long  height;
        unsigned short  colorPlane;
        unsigned short  bitColor;
        long  zipFormat;
        long  realSize;
        long  xPels;
        long  yPels;
        long  colorUse;
        long  colorImportant;
    }InfoHead;

    BmpHead m_BMPHeader={0};
    InfoHead  m_BMPInfoHeader={0};
    char bfType[2]={'B','M'};
    int header_size=sizeof(bfType)+sizeof(BmpHead)+sizeof(InfoHead);

    FILE *fp_bmp=NULL;
    if((fp_bmp=fopen(file,"wb"))==NULL)
    {
        cerr << "Error: Cannot open output BMP file." << endl;
        return -1;
    }

    m_BMPHeader.imageSize=3*width*height+header_size;
    m_BMPHeader.startPosition=header_size;

    m_BMPInfoHeader.Length=sizeof(InfoHead);
    m_BMPInfoHeader.width=width;
    m_BMPInfoHeader.height=height;
    m_BMPInfoHeader.colorPlane=1;
    m_BMPInfoHeader.bitColor=24;
    m_BMPInfoHeader.realSize=3*width*height;

    fwrite(bfType,1,sizeof(bfType),fp_bmp);
    fwrite(&m_BMPHeader,1,sizeof(m_BMPHeader),fp_bmp);
    fwrite(&m_BMPInfoHeader,1,sizeof(m_BMPInfoHeader),fp_bmp);

    fwrite(pBmp,3*width*height,1,fp_bmp);
    fclose(fp_bmp);
    return 0;
}

//---------------------------------------------------------------------------------------
void raw2bmp(uint8_t *pRaw, uint8_t *pBmp, int bayer, int w, int h)
{
    int r,gr,gb,b;
    switch (bayer)
    {
    case 1: r=3; gr=1; gb=2; b=0; break;//BGGR
    case 2: r=0; gr=1; gb=2; b=3; break;//RGGB
    case 3: r=2; gr=0; gb=3; b=1; break;//GBRG
    case 4: r=1; gr=0; gb=3; b=2; break;//GRBG
    case 5: //Gray
    default: r=0; gr=0; gb=0; b=0; break;
    }

    for (int y = h - 1; y >= 0; y--)
    {
        uint8_t P[4];
        for (int x = 0; x < w; x++)
        {
            if (x < w-2)
            {
                int Y = (y)&(~0x01);
                P[0] = pRaw[(Y)*w + ((x+1)&(~0x01))];
                P[1] = pRaw[(Y)*w + (x&(~0x01)) + 1];
                P[2] = pRaw[(Y+1)*w + ((x+1)&(~0x01))];
                P[3] = pRaw[(Y+1)*w + (x&(~0x01)) + 1];
            }
            *pBmp++ = P[b];
            *pBmp++ = (P[gr]+P[gb])/2;
            *pBmp++ = P[r];
        }
    }
}

//---------------------------------------------------------------------------------------
int convert_rawfiles_to_bmpfiles(struct Settings *settings)
{
    int w = settings->width, h = settings->height;
    int len = w*h;

    vector<string> files;
    int file_num = traverse_dir(settings->rawdir, files, "raw");
    cout << "we have " << file_num << "files to be convert!" << endl;

    _mkdir("output");

    uint8_t *pRaw = new uint8_t[w*h];
    uint8_t *pBmp = new uint8_t[w*h*3];

    for (int i = 0; i < file_num; i++)
    {
        string file = files.at(i);
        cout << "start to convert " << file << "...";

        file_read(file.c_str(), 0, pRaw, len);
        raw2bmp(pRaw, pBmp, settings->bayer, w, h);

        char drive[MAX_PATH], dir[MAX_PATH], fname[MAX_PATH], ext[MAX_PATH];
        _splitpath(file.c_str(), drive, dir, fname, ext);

        char cmd[MAX_PATH];
        sprintf(cmd, "output\\%s.bmp", fname);
        save_image_as_bmp(cmd, pBmp, w, h);
        cout << "   done!" << endl;
    }

    delete[] pRaw;
    delete[] pBmp;

    cout << "In all, "<< file_num << " files has been succeeded to be"
        "converted to bmp files!" << endl;
    return 0;
}
//---------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
    Settings setting;
    if (get_settings(setting) < 0)
    {
        cerr << "failed to parset settings file!" << endl;
        return -1;
    }
    convert_rawfiles_to_bmpfiles(&setting);
    return 0;
}
//---------------------------------------------------------------------------------------
